/* $RCSFile: MenuSiteMapProvider.cs $
 * Apache license v2 2011 by Christian Surieux
 * $Source: ?/ADNCRM.Core/Providers/MenuSiteMapProvider.cs $
 * $log$
 * Revision 6 2010/08/06 19:56:28  christian.surieux
 *   Added Log to All cs files
 */

using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;
using System.Collections.Specialized;
using System.Data;
using System.Security.Permissions;
using System.Web;
using System.Web.UI;
using System.Web.Util;
using System.Globalization;

using AddonNice.Core;
using AddonNice.Diagnostics;
using AddonNice.Design;
using AddonNice.Configuration;
using AddonNice.Configuration.Settings;
using AddonNice.Security;
using AddonNice.Settings;
using  AddonNice.UI.WebControls;
using AddonNice.Helpers;
using Rewrite.NET;

using Trace2    =   System.Diagnostics.Trace;

namespace AddonNice.Core.Providers
{
    [AspNetHostingPermission(SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Minimal)]
    public class MenuSiteMapProvider : SiteMapProvider
    {
        #region params

        const string strParentPageID    = "ParentPageID";


        private BindOption _bind        = BindOption.BindOptionTop;
        private int _definedParentTab   = -1;
        private bool _useTabNameInUrl   = true;

        PortalSettings _portalSettings;
        PortalSettings portalSettings
        {
            get
            {
                if (_portalSettings == null)
                    _portalSettings = (PortalSettings)HttpContext.Current.Items[StStr.strPortalSettings];
                return _portalSettings;
            }
        }

        /// <summary> 
        /// Indicates if control should show the tabname in the url 
        /// </summary> 
        public bool UseTabNameInUrl
        {
            get { return _useTabNameInUrl; }
            set { _useTabNameInUrl = value; }
        }
        public BindOption Bind
        {
            get { return _bind; }
            set
            {
                if (_bind != value)
                {
                    _bind = value;
                }
            }
        }

        /// <summary>
        /// Defines the parentPageID when using BindOptionDefinedParent
        /// </summary>
        public int ParentPageID
        {
            get { return _definedParentTab; }
            set
            {
                if (_definedParentTab != value)
                {
                    _definedParentTab = value;
                }
            }
        }

        /// <summary>
        /// This is the real Parent used for menu construct when in mode FullMenu == false
        /// </summary>
        int _CurrentParentPageID = -1;
        int CurrentParentPageID
        {
            get
            {
                if ( NavTraceSwitch.Sw.TraceVerbose )
                    Trace2.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]MenuSiteMapProvider CurrentParentPageID _CurrentParentPageID: {0}", _CurrentParentPageID),NavTraceSwitch.Sw.Info);
                return _CurrentParentPageID;
            }
            set
            {
                _CurrentParentPageID = value;
            }
        }

        #endregion params

        #region StaticSiteMapProvider
        internal string AppDomainAppVirtualPathWithTrailingSlash    =  VirtualPathUtility.AppendTrailingSlash(HttpRuntime.AppDomainAppVirtualPath);

        internal static SiteMapNodeCollection Empty                 =   new SiteMapNodeCollection();
        internal object _lock                                       =   new object();

        // Table maps nodes to their child node collections
        private Hashtable _childNodeCollectionTable;
        internal IDictionary ChildNodeCollectionTable
        {
            get
            {
                if (_childNodeCollectionTable == null)
                {
                    lock (_lock)
                    {
                        if (_childNodeCollectionTable == null)
                        {
                            _childNodeCollectionTable = new Hashtable();
                        }
                    }
                }

                return _childNodeCollectionTable;
            }
        }

        // Case sensitive table that maps key to sitemap node.
        private Hashtable _keyTable;
        internal IDictionary KeyTable
        {
            get
            {
                if (_keyTable == null)
                {
                    lock (_lock)
                    {
                        if (_keyTable == null)
                        {
                            _keyTable = new Hashtable();
                        }
                    }
                }
                return _keyTable;
            }
        }

        // Table maps nodes to their parent nodes
        private Hashtable _parentNodeTable;
        internal IDictionary ParentNodeTable
        {
            get
            {
                if (_parentNodeTable == null)
                {
                    lock (_lock)
                    {
                        if (_parentNodeTable == null)
                        {
                            _parentNodeTable = new Hashtable();
                        }
                    }
                }

                return _parentNodeTable;
            }
        }

        // Case insensitive table that maps url to sitemap node.
        private Hashtable _urlTable;
        internal IDictionary UrlTable
        {
            get
            {
                if (_urlTable == null)
                {
                    lock (_lock)
                    {
                        if (_urlTable == null)
                        {
                            _urlTable = new Hashtable(StringComparer.OrdinalIgnoreCase);
                        }
                    }
                }

                return _urlTable;
            }
        }

        #endregion StaticSiteMapProvider

        private SiteMapNode rootNode = null;

        // Implement a default constructor.
        public MenuSiteMapProvider() { }

        // Some basic state to help track the initialization state of the provider.
        private bool _initialized =  false;
        public virtual bool IsInitialized
        {
            get
            {
                return _initialized;
            }
        }
        // Return the root node of the current site map.
        public override SiteMapNode RootNode
        {
            get
            {
                return BuildSiteMap();
            }
        }
        
        protected override SiteMapNode GetRootNodeCore()
        {
            return RootNode;
        }

        /// <summary>
        /// Initialize is used to initialize the properties and any state that the
        /// AccessProvider holds, but is not used to build the site map.
        /// The site map is built when the BuildSiteMap method is called.
        /// </summary>
        public override void Initialize(string name, NameValueCollection attributes)
        {
            if ( NavTraceSwitch.Sw.TraceVerbose )
                Trace2.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]MenuSiteMapProvider Initialize BEG name: {0}, attributes: {1}, IsInitialized: {2}", name, attributes.Count,IsInitialized),NavTraceSwitch.Sw.Info);
            if (IsInitialized)
                return;

            base.Initialize(name, attributes);

            string BindStr              =   attributes["Bind"];
            if (string.IsNullOrEmpty(BindStr))
                Bind                    =   BindOption.BindOptionTop;
            else
            {
                BindStr                 =   BindStr.ToLower();
                if (BindStr == "bindoptiontop")
                    Bind                =   BindOption.BindOptionTop;
                else if (BindStr == "bindoptioncurrentchilds")
                    Bind                =   BindOption.BindOptionCurrentChilds;
                else if (BindStr == "bindoptionsubtabsibling")
                    Bind                =   BindOption.BindOptionSubtabSibling;
                else if (BindStr == "bindoptionchildren")
                    Bind                =   BindOption.BindOptionChildren;
                else if (BindStr == "bindoptionsibling")
                    Bind                =   BindOption.BindOptionSiblings;
                else if (BindStr == "bindoptiondefinedparent")
                    Bind                =   BindOption.BindOptionDefinedParent;
                else if (BindStr == "bindoptiontabsibling")
                    Bind                =   BindOption.BindOptionTabSibling;
            }

            string strParentID          =   attributes[strParentPageID];
            if (string.IsNullOrEmpty(strParentID))
            {
                if (Bind == BindOption.BindOptionDefinedParent)
                {
                    string msg          =   string.Format("[E]MenuSiteMapProvider Initialize bind option BindOptionDefinedParent and no ParentPageID: {0}", Bind);
                    if ( NavTraceSwitch.Sw.TraceError )
                        Trace2.WriteLine( msg,NavTraceSwitch.Sw.Info);
                    throw new Exception(msg);
                }
            }
            else
            {
                ParentPageID            =   int.Parse(strParentID);
            }

            string strUseTabNameInUrl   =   attributes["UseTabNameInUrl"];
            if (!string.IsNullOrEmpty(strUseTabNameInUrl))
            {
                UseTabNameInUrl         =   bool.Parse(strUseTabNameInUrl);
            }
            // in order to have translated display, install a handler static on changeLanguage
            LangChooser.ChangeLanguageNoRedirect += new LangChooserEventHandler(LangChooser_ChangeLanguageNoRedirect);
            if ( NavTraceSwitch.Sw.TraceVerbose )
                Trace2.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]MenuSiteMapProvider Initialize Ok BindOption: {0}, ParentPageID: {1}, UseTabNameInUrl: {2}", Bind, ParentPageID, UseTabNameInUrl),NavTraceSwitch.Sw.Info);
            _initialized                =   true;
        }

        // On a change language, we need to rebuild
        void LangChooser_ChangeLanguageNoRedirect(object sender,LangChooserEventArgs e)
        {
            Clear();
        }

        /// <summary>
        /// SiteMapProvider and StaticSiteMapProvider methods that this derived class must override.
        /// Clean up any collections or other state that an instance of this may hold.
        /// </summary>
        protected void Clear()
        {
            lock (_lock)
            {
                rootNode    =  null;
                if (_childNodeCollectionTable != null)
                    _childNodeCollectionTable.Clear();

                if (_urlTable != null)
                    _urlTable.Clear();

                if (_parentNodeTable != null)
                    _parentNodeTable.Clear();

                if (_keyTable != null)
                    _keyTable.Clear();
            }
        }

        
        /// <summary>
        /// Find sitemapnode in current provider
        /// </summary>
        public override SiteMapNode FindSiteMapNodeFromKey(string key)
        {
            if ( NavTraceSwitch.Sw.TraceVerbose )
                Trace2.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]MenuSiteMapProvider FindSiteMapNodeFromKey key: {0}", key),NavTraceSwitch.Sw.Info);
            SiteMapNode result  =   base.FindSiteMapNodeFromKey(key);

            if (result == null)
            {
                result          =   (SiteMapNode)KeyTable[key];
            }

            return ReturnNodeIfAccessible(result);
        }

        /// <summary>
        /// Find sitemapnode in current provider
        /// </summary>
        public override SiteMapNode FindSiteMapNode(string rawUrl)
        {
            if ( rawUrl == null )
            {
                string msg          =   "[E]MenuSiteMapProvider FindSiteMapNode rawUrl null.";
                if ( NavTraceSwitch.Sw.TraceError )
                    Trace2.WriteLine( msg,NavTraceSwitch.Sw.Info);
                throw new ArgumentNullException("rawUrl",msg);
            }

            // URL needs to be trimmed.
            rawUrl                  =   rawUrl.Trim();

            if (rawUrl.Length == 0)
            {
                string msg          =   "[V]MenuSiteMapProvider FindSiteMapNode rawUrl empty.";
                if ( NavTraceSwitch.Sw.TraceVerbose )
                    Trace2.WriteLine( msg,NavTraceSwitch.Sw.Info);
                return null;
            }
            if ( NavTraceSwitch.Sw.TraceVerbose )
                Trace2.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]MenuSiteMapProvider FindSiteMapNode rawUrl: {0}", rawUrl),NavTraceSwitch.Sw.Info);

            string virtualPath      =   rawUrl;
            int PageId              =   -1;
            int qs                  =   rawUrl.IndexOf(StStr.strCurrentParentPageID+"=");
            if (qs != -1)
            {
                string strPageId    =   rawUrl.Substring(qs + 20).Trim();
                int idx             =   strPageId.IndexOf('&');
                if (idx != -1)
                {
                    strPageId       =   strPageId.Substring(0, idx);
                }
                PageId              =   int.Parse(strPageId);
                if ( NavTraceSwitch.Sw.TraceVerbose )
                    Trace2.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]MenuSiteMapProvider FindSiteMapNode PageId: {0}", PageId),NavTraceSwitch.Sw.Info);
            }
            else
            {
                string[] tb         =   rawUrl.Split(new char[] { '/' },StringSplitOptions.RemoveEmptyEntries);
                // CurrentParentPageID is 2 parts before last part
                if (tb.Length > 3)
                {
                    string strPageId =  tb[tb.Length - 3];
                    if ( NavTraceSwitch.Sw.TraceVerbose )
                        Trace2.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]MenuSiteMapProvider FindSiteMapNode strPageId: {0}", strPageId),NavTraceSwitch.Sw.Info);
                    int idx         =   strPageId.IndexOf("__");
                    if (idx != -1)
                    {
                        strPageId   =   strPageId.Substring(idx + 2);
                        if (Char.IsNumber(strPageId[0]))
                        {
                            PageId  =   int.Parse(strPageId);
                            qs      =   0;
                            if ( NavTraceSwitch.Sw.TraceVerbose )
                                Trace2.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]MenuSiteMapProvider FindSiteMapNode CurrentParentPageID: {0}", PageId),NavTraceSwitch.Sw.Info);
                        }
                    }
                }
            }
            if ( qs == -1 )
            {
                // try to decode string with rewrite.net
                if ( NavTraceSwitch.Sw.TraceVerbose )
                    Trace2.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]MenuSiteMapProvider FindSiteMapNode ParentPageId not found trying url rewritter rawurl: {0}", rawUrl),NavTraceSwitch.Sw.Info);

                Rewrite.NET.Rewrite rweng   =   new Rewrite.NET.Rewrite();
                bool decoded                =   rweng.DecodeUrl(HttpContext.Current.ApplicationInstance,ref rawUrl);
                if ( NavTraceSwitch.Sw.TraceVerbose )
                    Trace2.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]MenuSiteMapProvider FindSiteMapNode ParentPageId after rewritter decoded: {0}, rawurl: {1}",decoded,rawUrl),NavTraceSwitch.Sw.Info);
                qs                          =   rawUrl.IndexOf("pageid=",StringComparison.InvariantCultureIgnoreCase);
                if ( qs != -1 )
                {
                    string strPageId        =   rawUrl.Substring(qs + 7).Trim();
                    int idx                 =   strPageId.IndexOf('&');
                    if (idx != -1)
                    {
                        strPageId           =   strPageId.Substring(0, idx);
                    }
                    PageId                  =   int.Parse(strPageId);
                    if ( NavTraceSwitch.Sw.TraceVerbose )
                        Trace2.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]MenuSiteMapProvider FindSiteMapNode PageId: {0}", PageId),NavTraceSwitch.Sw.Info);
                }
            }
            
            if ( qs == -1 )
            {
                qs                          =   rawUrl.IndexOf('?');
                if (qs != -1)
                {
                    virtualPath             =   rawUrl.Substring(0, qs);
                }

                // Make sure it is an app absolute url
                if (VirtualPathUtility.IsAppRelative(virtualPath))
                {
                    virtualPath             =   VirtualPathUtility.ToAbsolute(virtualPath);
                }

                if (qs != -1)
                {
                    virtualPath             +=  rawUrl.Substring(qs);
                }
                if ( NavTraceSwitch.Sw.TraceVerbose )
                    Trace2.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]MenuSiteMapProvider FindSiteMapNode virtualPath: {0}", virtualPath),NavTraceSwitch.Sw.Info);
            }

            // PageID = 0 is a non existing address meaning first page of site
            // so we must replace it by the portalActivePage which is the actuallyloaded page
            if ( (( PageId == StStr.PageIDNotAssigned ) || ( PageId == -1 )) && ( portalSettings != null ) && ( portalSettings.ActivePageSettings != null ) )
                PageId                      =   portalSettings.ActivePageSettings.PageID;
            BuildSiteMap();
            if ( PageId == -1 )
                return ReturnNodeIfAccessible((SiteMapNode)UrlTable[virtualPath]);
            if ( PageId == StStr.PageIDNotAssigned )
                return ReturnNodeIfAccessible(RootNode);
            return ReturnNodeIfAccessible((SiteMapNode)KeyTable[PageId.ToString()]);
        }

        
        /// <summary>
        /// Return readonly child node collection
        /// </summary>
        public override SiteMapNodeCollection GetChildNodes(SiteMapNode node)
        {
            if (node == null)
            {
                string msg                      =   "[E]MenuSiteMapProvider GetChildNodes node is null";
                if ( NavTraceSwitch.Sw.TraceVerbose )
                    Trace2.WriteLine( msg,NavTraceSwitch.Sw.Info);
                throw new ArgumentNullException("node",msg);
            }
            if ( NavTraceSwitch.Sw.TraceVerbose )
                Trace2.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]MenuSiteMapProvider GetChildNodes node.Key: {0}", node.Key),NavTraceSwitch.Sw.Info);

            BuildSiteMap();
            SiteMapNodeCollection collection    =   (SiteMapNodeCollection)ChildNodeCollectionTable[node];

            if (collection == null)
            {
                SiteMapNode childNodeFromKey    =   (SiteMapNode)KeyTable[node.Key];
                if (childNodeFromKey != null)
                {
                    collection                  =   (SiteMapNodeCollection)ChildNodeCollectionTable[childNodeFromKey];
                }
            }

            if (collection != null)
            {
                if (!SecurityTrimmingEnabled)
                {
                    return SiteMapNodeCollection.ReadOnly(collection);
                }

                HttpContext context             = HttpContext.Current;
                SiteMapNodeCollection trimmedCollection = new SiteMapNodeCollection(collection.Count);
                foreach (SiteMapNode subNode in collection)
                {
                    //if (subNode.IsAccessibleToUser(context))
                    {
                        trimmedCollection.Add(subNode);
                    }
                }

                return SiteMapNodeCollection.ReadOnly(trimmedCollection);
            }

            return Empty;
        }


        public override SiteMapNode GetParentNode(SiteMapNode node)
        {
            if (node == null)
            {
                string msg          =   "[E]MenuSiteMapProvider GetParentNode node is null";
                if ( NavTraceSwitch.Sw.TraceVerbose )
                    Trace2.WriteLine( msg,NavTraceSwitch.Sw.Info);
                throw new ArgumentNullException("node",msg);
            }
            if ( NavTraceSwitch.Sw.TraceVerbose )
                Trace2.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]MenuSiteMapProvider GetParentNode node.Key: {0}", node.Key),NavTraceSwitch.Sw.Info);

            BuildSiteMap();
            SiteMapNode parent      =   (SiteMapNode)ParentNodeTable[node];

            if (parent == null)
            {
                // Try to find the original node in the table using the key
                SiteMapNode fallbackNode = (SiteMapNode)KeyTable[node.Key];
                if (fallbackNode != null)
                {
                    parent          =   (SiteMapNode)ParentNodeTable[fallbackNode];
                }
            }

            // Try the parent providers.
            if (parent == null && ParentProvider != null)
            {
                parent              =   ParentProvider.GetParentNode(node);
            }
            // avoid infinite loop for pageId=0 with parentid null
            if ( parent == node )
                parent              =   null;
            return ReturnNodeIfAccessible(parent);
        }


        protected override void RemoveNode(SiteMapNode node)
        {
            if (node == null)
            {
                string msg              =   "[E]MenuSiteMapProvider RemoveNode node is null";
                if ( NavTraceSwitch.Sw.TraceVerbose )
                    Trace2.WriteLine( msg,NavTraceSwitch.Sw.Info);
                throw new ArgumentNullException("node",msg);
            }
            if ( NavTraceSwitch.Sw.TraceVerbose )
                Trace2.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]MenuSiteMapProvider RemoveNode node.Key: {0}", node.Key),NavTraceSwitch.Sw.Info);

            lock (_lock)
            {
                SiteMapNode oldParent   =     (SiteMapNode)ParentNodeTable[node];
                if (ParentNodeTable.Contains(node))
                    ParentNodeTable.Remove(node);

                if (oldParent != null)
                {
                    SiteMapNodeCollection collection = (SiteMapNodeCollection)ChildNodeCollectionTable[oldParent];
                    if (collection != null && collection.Contains(node))
                        collection.Remove(node);
                }

                string url              =   node.Url;
                if (url != null && url.Length > 0 && UrlTable.Contains(url))
                {
                    UrlTable.Remove(url);
                }

                string key              =   node.Key;
                if (KeyTable.Contains(key))
                {
                    KeyTable.Remove(key);
                }
            }
        }

        internal SiteMapNode ReturnNodeIfAccessible(SiteMapNode node)
        {
            if (node != null /*&& node.IsAccessibleToUser(HttpContext.Current)*/)
            {
                return node;
            }

            return null;
        }

        /// <summary>
        /// Build an in-memory representation from persistent
        /// storage, and return the root node of the site map.
        /// </summary>
        public SiteMapNode BuildSiteMap()
        {

            // Since the SiteMap class is static, make sure that it is
            // not modified while the site map is built.
            lock (_lock)
            {

                // If there is no initialization, this method is being
                // called out of order.
                if (!IsInitialized)
                {
                    const string msg    =   "[E]MenuSiteMapProvider BuildSiteMap BuildSiteMap called incorrectly.";
                    if ( NavTraceSwitch.Sw.TraceError )
                        Trace2.WriteLine( msg,NavTraceSwitch.Sw.Info);
                    throw new Exception(msg);
                }

                // If there is no root node, then there is no site map.
                if (rootNode == null)
                {
                    // Start with a clean state
                    Clear();

                    // Select the root node of the site map 
                    int rootNodeId                  =   0;

                    //List<PageStripDetails> Pages = GetInnerDataSource();
                    AuthorizedPageStripDetail Pages =   new AuthorizedPageStripDetail(Bind,ParentPageID,CurrentParentPageID);
                    ParentPageID                    =   Pages.ParentPageID;
                    CurrentParentPageID             =   Pages.CurrentParentPageID;

                    if ( NavTraceSwitch.Sw.TraceVerbose )
                        Trace2.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]MenuSiteMapProvider BuildSiteMap Pages.Cout: {0}", Pages.Count),NavTraceSwitch.Sw.Info);

                    if (Pages.Count <= 0)
                        return null;

                    bool isForm                     =   (Config.AuthenticationMode == System.Web.Configuration.AuthenticationMode.Forms );
                    IAddonNiceUser    curUser       =   PortalSettings.CurrentUser;
                    bool isAuthenticated            =   ( curUser != null ) ? !curUser.IsAnonymous : false;
                    if (ParentPageID == -1)
                    {
                        if ( ( Pages.Count == 1 )&& 
                            Pages[0].OkForMenus &&
                            ( isForm || (!isForm && !Pages[0].FormsOnly ) ) &&
                            ( isAuthenticated || (!isAuthenticated && !Pages[0].AuthenticatedOnly ) )
                            )
                        {
                                rootNodeId          =   Pages[0].ID;
                                rootNode            =   new SiteMapNode(this, rootNodeId.ToString(), BuildNodeUrl(rootNodeId),
                                        Pages[0].Name,Pages[0].Name);
                        }
                        else    
                        {
                            rootNodeId              =   0;
                            string strroot          =   LocalizeHelper.Localize("PROV_ROOT_LEVEL","Root");
                            rootNode                =   new SiteMapNode(this, rootNodeId.ToString(), BuildNodeUrl(rootNodeId),strroot,strroot);
                        }
                    }
                    else 
                    {
                        rootNodeId                  =   ParentPageID;
                        rootNode                    =   new SiteMapNode(this, rootNodeId.ToString(), BuildNodeUrl(rootNodeId),Pages[0].Name,Pages[0].Name);
                    }
                    for (int i = 0; i < Pages.Count; i++)
                    {
                        PageStripDetails tab        =   Pages[i];
                        if (!tab.OkForMenus)
                            continue;
                        if ( !isForm && tab.FormsOnly )
                            continue;
                        if ( !isAuthenticated && tab.AuthenticatedOnly )
                            continue;
                        SiteMapNode childNode       =   null;
                        if ( tab.ID != rootNodeId )
                                childNode           =   new SiteMapNode(this, tab.PageID.ToString(), BuildNodeUrl(tab.PageID), tab.PageKeyPhrase, tab.PageTitle);
                        else    childNode           =   rootNode;
                        RecurseNode(tab.Pages, childNode);
                        AddNode(childNode, rootNode);
                    }
                }
                return rootNode;
            }
        }

        protected override void AddNode(SiteMapNode node, SiteMapNode parentNode)
        {
            if (node == null)
            {
                string msg      =   "[E]MenuSiteMapProvider AddNode Null Node";
                if ( NavTraceSwitch.Sw.TraceError )
                    Trace2.WriteLine( msg,NavTraceSwitch.Sw.Info);
                throw new ArgumentNullException("node",msg);
            }

            lock (_lock)
            {
                bool validUrl   =   false;

                string url      =   node.Url;
                if (!String.IsNullOrEmpty(url))
                {
                    if (UrlTable[url] != null)
                    {
                        string msg = string.Format("[E]MenuSiteMapProvider AddNode url already in dictionary key:{0}", url);
                        if ( NavTraceSwitch.Sw.TraceError )
                            Trace2.WriteLine( msg,NavTraceSwitch.Sw.Info);
                        throw new InvalidOperationException(msg);
                    }
                    validUrl    =   true;
                }

                String key      =   node.Key;
                if (KeyTable.Contains(key))
                {
                    string msg = string.Format("[E]MenuSiteMapProvider AddNode Key already in dictionary key:{0}", key);
                    if ( NavTraceSwitch.Sw.TraceError )
                        Trace2.WriteLine( msg,NavTraceSwitch.Sw.Info);
                    throw new InvalidOperationException(msg);
                }

                KeyTable[key]   =   node;

                if (validUrl)
                {
                    UrlTable[url] = node;
                }

                if (parentNode != null)
                {
                    ParentNodeTable[node]                       =   parentNode;
                    if (ChildNodeCollectionTable[parentNode] == null)
                        ChildNodeCollectionTable[parentNode]    =   new SiteMapNodeCollection();
                    ((SiteMapNodeCollection)ChildNodeCollectionTable[parentNode]).Add(node);
                }
            }
        }

        protected SiteMapNode RecurseNode(PageStripDetailsList t, SiteMapNode nd)
        {
            // don't go under the limited depth
            if (t.Count <= 0)
                return nd;
            bool isForm                     =   (Config.AuthenticationMode == System.Web.Configuration.AuthenticationMode.Forms );
            IAddonNiceUser    curUser       =   PortalSettings.CurrentUser;
            bool isAuthenticated            =   ( curUser != null ) ? !curUser.IsAnonymous : false;
            for (int c = 0; c < t.Count; c++)
            {
                PageStripDetails tab        =   t[c];
                if ( NavTraceSwitch.Sw.TraceVerbose )
                    Trace2.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]MenuSiteMapProvider RecurseNode PageName: {0}, PageID: {1}", tab.PageName, tab.PageID),NavTraceSwitch.Sw.Info);
                if (tab.OkForMenus &&
                    ( isForm || ( !isForm && !tab.FormsOnly ) ) && 
                    ( isAuthenticated || ( !isAuthenticated && !tab.AuthenticatedOnly ) ) && 
                        PortalSecurity.IsUserInRoles(tab.AuthorizedRoles))
                {
                    SiteMapNode childNode   =   new SiteMapNode(this, tab.PageID.ToString(), BuildNodeUrl(tab.PageID), tab.PageKeyPhrase, tab.PageTitle);
                    RecurseNode(tab.Pages, childNode);
                    AddNode(childNode, nd);
                    if ( NavTraceSwitch.Sw.TraceVerbose )
                        Trace2.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]MenuSiteMapProvider RecursePanelItem Adding Key: {0},", nd.Key),NavTraceSwitch.Sw.Info);
                }
            }
            return nd;
        }

        /// <summary>
        /// This is no real url for the page
        /// We build url for menu installed on current page, so we build for current pageId with a parameter driving to requested page
        /// </summary>
        private string BuildNodeUrl(int id)
        {
            string url  =   string.Empty;
            // When binding to Menu, we must generate different urls, based on the presence of CurrentParentPageId to trace current level
            if ( id != 0 )
                 url    =   HttpUrlBuilder.BuildUrlWithCustomAttributes(portalSettings.ActivePageSettings.PageID, string.Format("{0}={1}", StStr.strCurrentParentPageID, id));
            else url    =   HttpUrlBuilder.BuildUrl(portalSettings.ActivePageSettings.PageID);
            if ( NavTraceSwitch.Sw.TraceVerbose )
                Trace2.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]MenuSiteMapProvider BuildNodeUrl id: {0}, url: {1}", id, url),NavTraceSwitch.Sw.Info);
            return url;
        }
    }
}